home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Unix / CNews / Source / relay / procart.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-03-27  |  15.0 KB  |  503 lines

  1. /*
  2.  * process a single incoming article
  3.  */
  4.  
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <sys/types.h>
  9. #include <sys/timeb.h>        /* solely for getindate call */
  10. #include "libc.h"
  11. #include "news.h"
  12. #include "active.h"
  13. #include "control.h"
  14. #include "headers.h"
  15. #include "article.h"
  16. #include "history.h"
  17. #include "io.h"
  18. #include "msgs.h"
  19. #include "ngmatch.h"
  20. #include "system.h"
  21. #include "transmit.h"
  22.  
  23. #define DAY (24L*60L*60L)
  24.  
  25. /*
  26.  * seconds of slop permitted: article dates may be this many seconds in the
  27.  * future.  It should be an hour, but for sites (e.g. in Australia) that
  28.  * emit local time incorrectly labelled as GMT.  They really should fix
  29.  * their software, but in the mean time, a day's slop will prevent their
  30.  * articles from being dropped.
  31.  */
  32. #define CLOCKSLOP DAY
  33.  
  34. /*
  35.  * COPYSIZE is the length of a bulk-copying buffer: the bigger the better,
  36.  * though fewer than 3% of articles exceed 8192 bytes (may 1988).
  37.  * It holds header lines first, and later holds bytes of the body.
  38.  * This buffer is allocated once at the start and never deallocated.
  39.  */
  40. #ifndef COPYSIZE
  41. #ifdef SMALLMEM
  42. #define COPYSIZE BUFSIZ        /* conserve memory at the expense of speed */
  43. #else
  44. #define COPYSIZE 8192        /* big enough even for worst-case 4.2bsd blocks */
  45. #endif                /* SMALLMEM */
  46. #endif                /* COPYSIZE */
  47.  
  48. /* imports */
  49. extern char *exclude;        /* for erik */
  50. extern long staledays;        /* from relaynews.c */
  51. extern boolean dupsokay;    /* from relaynews.c */
  52. extern void decline();
  53.  
  54. /* forwards */
  55. extern void tossorfile(), surveydamage(), reject(), prefuse(), uninsart();
  56. extern char *hdrcopy();
  57. FORWARD void copyart(), cpybody(), insart();
  58. FORWARD statust snuffmayreturn();
  59.  
  60. /*
  61.  * Copy the article on "in" to a temporary name in the news spool directory,
  62.  * unlink temp name; *or* copy into the final names, if known early enough.
  63.  * (Sets a_tmpf in or near hdrmunge() or hdrdump().)
  64.  * If the spool file opened, install the article it contains.
  65.  */
  66. statust
  67. cpinsart(in, inname, maxima, blvmax)
  68. FILE *in;
  69. register char *inname;
  70. long maxima;
  71. boolean blvmax;                /* believe maxima? */
  72. {
  73.     register struct article *artp;
  74.     register statust status;
  75.     struct article art;
  76.  
  77.     artp = &art;
  78.     artinit(artp);
  79.     artp->a_blvmax = blvmax;
  80.     artp->a_unread = maxima;
  81.  
  82.     /*
  83.      * copyart() may reject() the article, and may fill the disk.
  84.      * it calls fileart and logs rejected articles.  it may call uninsart.
  85.      */
  86.     copyart(artp, in, inname);
  87.  
  88.     if (artp->a_status&ST_REFUSED) {
  89.         /* no good ngs (in fileart) or reject()ed; not serious */
  90.         artp->a_status &= ~ST_REFUSED;
  91.         /* paranoia; shouldn't happen */
  92.         nnfclose(artp, &artp->a_artf, inname);
  93.     } else if (artp->a_artf == NULL) {
  94.         warning("can't open spool file `%s'", artp->a_tmpf);
  95.         artp->a_status |= ST_DROPPED;
  96.     } else {
  97.         nnfclose(artp, &artp->a_artf, inname);
  98.         insart(artp);    /* logs accepted art.s during transmission */
  99.         if (artp->a_status&ST_JUNKED) {    /* yer welcome, henry */
  100.             artp->a_status &= ~ST_JUNKED;
  101.             timestamp(stdout, (time_t *)NULL);
  102.             (void) printf(" %s j %s junked due to groups `%s'\n",
  103.                 sendersite(nullify(artp->h.h_path)),
  104.                 artp->h.h_msgid, artp->h.h_ngs);
  105.         }
  106.     }
  107.     status = artp->a_status;
  108.     artfree(artp);
  109.     return status;
  110. }
  111.  
  112. /*
  113.  * Copy the next charcnt bytes of "in" (may be not a disk file)
  114.  * to a permanent file under a (possibly) temporary name.
  115.  * After the headers are seen, accept or reject the article.
  116.  * If rejected and the headers fit in core, no files will be opened.
  117.  * Must munge certain headers on the way & remember certain values.
  118.  * hdrmunge() or hdrdump() sets art->a_tmpf & art->a_artf.
  119.  * Unlink art->a_tmpf, if a temporary link.
  120.  */
  121. /* ARGSUSED inname */
  122. STATIC void
  123. copyart(art, in, inname)
  124. register struct article *art;
  125. register FILE *in;
  126. char *inname;
  127. {
  128.     boolean installed = YES;
  129.     char *body;
  130.  
  131.     body = hdrcopy(art, in);
  132.     hdrdeflt(&art->h);
  133.     tossorfile(art, &installed);
  134.     /* assertion: header values (art->h) can be forgotten here */
  135.     cpybody(art, in, body);
  136.     surveydamage(art, &installed);
  137. }
  138.  
  139. /*
  140.  * The loop copies header lines from input to output or a
  141.  * header output cache.  On exit, hdr will contain the first
  142.  * non-header line, if any, left over from the end of header copying.
  143.  *
  144.  * Some people think the loop is ugly; I'm not sure why.
  145.  * If the byte count is positive, read a line; if it doesn't return
  146.  * EOF and is a header, then adjust byte count, stash and munge headers.
  147.  * strlen(line) must be computed before hdrstash is called,
  148.  * as hdrstash (and thus hdrdigest) removes newlines.
  149.  */
  150. char *                    /* first body line, from gethdr */
  151. hdrcopy(art, in)
  152. register struct article *art;
  153. FILE *in;
  154. {
  155.     register char *hdr = NULL;
  156.     long limit = art->a_unread + SIZENUL;
  157.     int is_hdr = NO;
  158.  
  159.     /*
  160.      * TODO: Cope with NULs in input, which bugger fgets and friends
  161.      * and throw off our byte count, thus buggering unbatching.
  162.      */
  163.     while (limit > SIZENUL && (hdr = gethdr(in, &limit, &is_hdr)) != NULL &&
  164.         is_hdr) {
  165.             hdrdigest(art, hdr, strlen(hdr));
  166.         hdr = NULL;            /* freed inside gethdr */
  167.     }
  168.     /* If we read a body line, gethdr has adjusted limit appropriately. */
  169.     art->a_unread = limit - SIZENUL;
  170.     /*
  171.      * RFC 822 defines the message header as ending at a blank line,
  172.      * *not* at the first line that cannot syntactically be a header nor
  173.      * a header continuation.  As a result of this stunning bit of
  174.      * brilliance, we can end up with non-header lines in the message
  175.      * header, though they are illegal.
  176.      *
  177.      * Don't print anything if this article has already been refused.
  178.      */
  179.     if (!is_hdr && hdr != NULL && *hdr != '\n' &&
  180.         !(art->a_status&ST_REFUSED)) {
  181.         register char *hdrnonl = strsave(hdr);
  182.  
  183. #ifdef notdef
  184.         art->a_badhdr = YES;    
  185. #endif
  186.         trim(hdrnonl);
  187.         decline(art);
  188.         prefuse(art);
  189.         (void) printf(
  190.         "article \"header\" contains non-RFC-1036-header line `%s'\n",
  191.             hdrnonl);
  192.         free(hdrnonl);
  193.     }
  194.     /* if is_hdr, there is no body: header fills limit */
  195.     return (is_hdr? NULL: hdr);
  196. }
  197.  
  198. /*
  199.  * Either reject the article described by art, or accept it and file it.
  200.  * If rejecting it, remove any links and give back assigned #'s
  201.  * (art->a_artf may still be open; arguably uninsart should close it).
  202.  * If accepting it, dump any saved headers and file the article.
  203.  * Unlink art->a_tmpf if it's a temporary link.
  204.  */
  205. void
  206. tossorfile(art, installedp)
  207. register struct article *art;
  208. boolean *installedp;
  209. {
  210.     reject(art);                /* duplicate, etc.? */
  211.     if (art->a_status&(ST_DROPPED|ST_REFUSED)) {
  212.         uninsart(art);
  213.         *installedp = NO;
  214.     } else
  215.         hdrdump(art, ALLHDRS);        /* ALLHDRS triggers fileart */
  216.  
  217.     if (art->a_unlink) {
  218.         /* a_tmpf has had links made to it, so it can be removed. */
  219.         if (unlink(art->a_tmpf) < 0) {
  220.             warning("copyart can't unlink `%s'", art->a_tmpf);
  221.             art->a_status |= ST_ACCESS;
  222.         }
  223.         art->a_unlink = NO;        /* caution */
  224.     }
  225. }
  226.  
  227. /*
  228.  * Copy article body.
  229.  * body will contain the first non-header line, if any,
  230.  * left over from the end of header copying.  Write it.
  231.  * Copy at most COPYSIZE bytes of body at a time and exactly art->a_unread
  232.  * bytes in total, barring EOF or a full disk. Then "block" is no longer needed.
  233.  * Force the article to disk, mostly for the benefit of control message
  234.  * processing.
  235.  *
  236.  * The copying buffer, block, is static because it is used repeatedly
  237.  * and persists through most of execution, so dynamic allocation
  238.  * and deallocation seems wasteful, but also for the benefit
  239.  * of compilers for odd machines (e.g. PE, 370s) which make
  240.  * implementing "large" automatic arrays difficult.
  241.  */
  242. STATIC void
  243. cpybody(art, in, body)
  244. register struct article *art;
  245. FILE *in;
  246. register char *body;
  247. {
  248.     register int readcnt;
  249.     static char block[COPYSIZE];
  250.  
  251.     if (body != NULL) {            /* read too far? */
  252.         register int bodylen = strlen(body);
  253.  
  254.         if (art->a_artf != NULL &&
  255.             fwrite(body, 1, bodylen, art->a_artf) != bodylen)
  256.             fulldisk(art, spoolnm(art));
  257.         art->a_charswritten += bodylen;
  258.     }
  259.     for (; art->a_unread > 0 && !(art->a_status&ST_NEEDATTN) && !feof(in) &&
  260.         (readcnt = fread(block, 1, (int)min(art->a_unread, COPYSIZE), in)) >
  261.         0; art->a_unread -= readcnt, art->a_charswritten += readcnt)
  262.         if (art->a_artf != NULL &&
  263.             fwrite(block, 1, readcnt, art->a_artf) != readcnt)
  264.             fulldisk(art, spoolnm(art));
  265.     if (art->a_artf != NULL && fflush(art->a_artf) == EOF)
  266.         fulldisk(art, spoolnm(art));
  267. }
  268.  
  269. /*
  270.  * If not yet uninstalled, and the disk filled (or the news system was found
  271.  * to be otherwise unwell), uninstall this article
  272.  * to remove any (zero-length) links and decrement the active article number.
  273.  * The ST_NEEDATTN status will prevent a history entry being generated later.
  274.  */
  275. void
  276. surveydamage(art, installedp)
  277. register struct article *art;
  278. register boolean *installedp;
  279. {
  280.     if (art->a_unread > 0 && art->a_blvmax) {
  281.         (void) fprintf(stderr, "%s: article %s short by %ld bytes\n",
  282.             progname, (art->h.h_msgid != NULL? art->h.h_msgid: ""),
  283.             (long)art->a_unread);
  284.         art->a_status |= ST_SHORT;  /* NB.: don't uninstall this art. */
  285.     }
  286.     if (*installedp && art->a_status&ST_NEEDATTN) {
  287.         uninsart(art);
  288.         *installedp = NO;
  289.     }
  290. #ifdef WATCHCORE
  291.     {
  292.         char stbot;
  293.         extern char *sbrk();
  294.  
  295.         printf("debugging memory use: top of data=%u",
  296.             (unsigned)sbrk(0));
  297.         printf(", bottom of stack=%u\n", (unsigned)&stbot);
  298.     }
  299. #endif
  300. }
  301.  
  302. /*
  303.  * If nothing has gone wrong yet,
  304.  * install the article on art->a_tmpf or art->a_files:
  305.  * The article should have been accepted and filed in copyart().
  306.  * Add history entries for the article.  Log arrival.
  307.  * Transmit the article to our neighbours.
  308.  * Process control mess(age)es.  ctlmsg can call transmit(fakeart,x)
  309.  * and generate log lines for cancels and ihave/sendme.
  310.  */
  311. STATIC void
  312. insart(art)
  313. register struct article *art;
  314. {
  315.     if (!(art->a_status&(ST_DROPPED|ST_REFUSED|ST_NEEDATTN))) {
  316.         if (!art->a_filed)            /* paranoia */
  317.             (void) fprintf(stderr, "%s: %s not filed by copyart!\n",
  318.                 progname, art->h.h_msgid);
  319.         if (dupsokay) {
  320.             time_t now;
  321.  
  322.             timestamp(stdout, &now);
  323.             if (printf(" %s + %s", /* TODO: special code for dup? */
  324.                 sendersite(nullify(art->h.h_path)),
  325.                 nullify(art->h.h_msgid)) == EOF)
  326.                 fulldisk(art, "stdout");
  327.         } else
  328.             history(art, STARTLOG);    /* history may be unwritable */
  329.         if (art->a_status&(ST_DROPPED|ST_REFUSED|ST_NEEDATTN)) {
  330.             uninsart(art);        /* t'was; can't keep article */
  331.             (void) putchar('\n');    /* ends the log line */
  332.         } else {
  333.             /* transmit() writes system names on stdout */
  334.             transmit(art, exclude);
  335.             (void) putchar('\n');    /* ends the log line */
  336.             ctlmsg(art);        /* NCMP */
  337.         }
  338. #ifdef FLUSHLOG
  339.         (void) fflush(stdout);        /* crash-proofness */
  340. #endif
  341.     }
  342.     art->a_status &= ~ST_REFUSED;    /* refusal is quite casual & common */
  343. }
  344.  
  345. /*
  346.  * Reject articles.  This can be arbitrarily picky.
  347.  * Only the headers are used to decide, so this can be called before
  348.  * the article is filed but after all the headers are read.
  349.  * Try to put the fastest tests first, especially if they often result
  350.  * in rejections.
  351.  */
  352. void
  353. reject(art)
  354. register struct article *art;
  355. {
  356.     register struct headers *hdrs = &art->h;
  357.     register char *ngs = hdrs->h_ngs;
  358.     register char *errstr;
  359.     register time_t date;
  360.     static time_t now, datestale;
  361.     extern time_t getindate();
  362.  
  363.     if (art->a_status&ST_REFUSED)
  364.         return;            /* already rejected */
  365.     if (now == 0) {
  366.         now = time(&now);
  367.         datestale = now - staledays*DAY;
  368.     }
  369.     errstr = hdrreq(hdrs);
  370.     if (errstr != NULL) {
  371.         prefuse(art);
  372.         (void) fputs(errstr, stdout);
  373. #ifdef notdef
  374.     } else if (art->a_badhdr) {
  375.         prefuse(art);
  376.         (void) fputs("article \"header\" contains non-header line\n",
  377.             stdout);
  378. #endif
  379.     } else if (!msgidok(art))
  380.         (void) putchar('\n');    /* msgidok complained; end log line */
  381.     else if (hdrs->h_approved == NULL && moderated(ngs)) {
  382.         prefuse(art);
  383.         (void) printf("unapproved article in moderated group(s) `%s'\n",
  384.             ngs);
  385.     } else if ((date = getindate(hdrs->h_date, (struct timeb *)NULL)) ==
  386.         -1) {
  387.         prefuse(art);
  388.         (void) printf("unparsable Date: `%s'\n", hdrs->h_date);
  389.     } else if (date > now + CLOCKSLOP) {
  390.         prefuse(art);
  391.         (void) printf("Date: too far in the future: `%s'\n",
  392.             hdrs->h_date);
  393.     } else if (staledays > 0 && date < datestale) {
  394.         prefuse(art);
  395.         (void) printf("ancient date `%s'\n", hdrs->h_date);
  396.     } else if (strchr(ngs, ' ') != NULL) {
  397.         prefuse(art);
  398.         (void) printf("space in groups `%s'\n", ngs);
  399.     } else if (alreadyseen(hdrs->h_msgid)) {
  400.         if (dupsokay)
  401.             return;
  402.         prefuse(art);
  403.         (void) fputs("duplicate\n", stdout);
  404.     } else if (hopcount(hdrs->h_path) > 0 &&
  405.         !ngpatmat(oursys()->sy_trngs, ngs)) {
  406.         extern boolean histreject;
  407.  
  408.         /*
  409.          * non-local article, with all bad groups.
  410.          * (local articles with bad groups will be bounced
  411.          * by fileart when the groups aren't in active.)
  412.          */
  413.         if (histreject)
  414.             history(art, NOLOG);
  415.         prefuse(art);
  416.         (void) printf("no subscribed groups in `%s'\n", ngs);
  417.     } else
  418.         return;            /* art was accepted */
  419.     decline(art);
  420. }
  421.  
  422. /*
  423.  * print the leader of a refusal message about the article in "art".
  424.  */
  425. void
  426. prefuse(art)
  427. register struct article *art;
  428. {
  429.     timestamp(stdout, (time_t *)NULL);
  430.     (void) printf(" %s - %s ", sendersite(nullify(art->h.h_path)),
  431.         nullify(art->h.h_msgid));
  432. }
  433.  
  434. /*
  435.  * "Uninstall" an article: remove art->a_files (permanent names) and
  436.  * a_tmpf (temporary name if a_unlink set), and return assigned article #'s.
  437.  * If a_unlink isn't set, a_tmpf is a copy of the first link in art->a_files.
  438.  * Must be called before history() is called (or after it has failed),
  439.  * else there will be a history entry for the article, but no spool files.
  440.  * insart() need not be called first.
  441.  */
  442. void
  443. uninsart(art)
  444. register struct article *art;
  445. {
  446.     if (art->a_unlink && art->a_tmpf != NULL) {
  447.         (void) unlink(art->a_tmpf);    /* I don't wanna know... */
  448.         art->a_unlink = NO;
  449.     }
  450.     /* return article numbers (YES) & ignore unlink errors */
  451.     (void) snuffmayreturn(art->a_files, YES);
  452. }
  453.  
  454. statust
  455. snufffiles(filelist)        /* just unlink all files in filelist */
  456. char *filelist;
  457. {
  458.     /* don't return article numbers (NO) & return unlink errors */
  459.     return snuffmayreturn(filelist, NO);
  460. }
  461.  
  462. /*
  463.  * Unlink all files in filelist, and optionally return article numbers.
  464.  * When removing a link, note any failure, but don't issue an error message.
  465.  * For one thing, cancel controls fail routinely because the article has been
  466.  * removed manually or never existed (a previous cancel arrived before its
  467.  * subject and generated a fake history entry).
  468.  */
  469. STATIC statust
  470. snuffmayreturn(filelist, artret)
  471. char *filelist;
  472. boolean artret;        /* return article numbers & note unlink errors? */
  473. {
  474.     register statust status = ST_OKAY;
  475.     register char *arts, *spacep, *slashp, *artnm;
  476.  
  477.     /* this is a deadly tedious job and I really should automate it */
  478.     for (arts = filelist; arts != NULL && arts[0] != '\0';
  479.          arts = (spacep == NULL? NULL: spacep+1)) {
  480.         spacep = strchr(arts, ' ');
  481.         if (spacep != NULL)
  482.             spacep[0] = '\0';    /* will be restored below */
  483.         artnm = strsave(arts);
  484.         if (spacep != NULL)
  485.             spacep[0] = ' ';    /* restore space */
  486.  
  487.         slashp = strchr(artnm, FNDELIM);
  488.         if (slashp != NULL)
  489.             slashp[0] = '\0';    /* will be restored below */
  490.         if (artret)
  491.             /* prevartnum will complain on i/o error to active */
  492.             (void) prevartnum(artnm); /* return assigned # */
  493.         if (slashp != NULL)
  494.             slashp[0] = FNDELIM;    /* restore slash */
  495.  
  496.         mkfilenm(artnm);
  497.         if (unlink(artnm) < 0)
  498.             status |= ST_ACCESS;
  499.         free(artnm);
  500.     }
  501.     return status;
  502. }
  503.